home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / pcl / cl-nd-cl.lha / clue / clio / buffer.lisp next >
Lisp/Scheme  |  1992-05-26  |  37KB  |  955 lines

  1. ;;; -*- Mode:Lisp; Package:CLIO-OPEN; Base:10; Lowercase:T; Syntax:Common-Lisp -*-
  2.  
  3.  
  4. ;;;----------------------------------------------------------------------------------+
  5. ;;;                                                                                  |
  6. ;;;                          TEXAS INSTRUMENTS INCORPORATED                          |
  7. ;;;                                  P.O. BOX 149149                                 |
  8. ;;;                             AUSTIN, TEXAS 78714-9149                             |
  9. ;;;                                                                                  |
  10. ;;;             Copyright (C) 1990, 1990 Texas Instruments Incorporated.             |
  11. ;;;                                                                                  |
  12. ;;; Permission is granted to any individual or institution to use, copy, modify, and |
  13. ;;; distribute this software, provided that  this complete copyright and  permission |
  14. ;;; notice is maintained, intact, in all copies and supporting documentation.        |
  15. ;;;                                                                                  |
  16. ;;; Texas Instruments Incorporated provides this software "as is" without express or |
  17. ;;; implied warranty.                                                                |
  18. ;;;                                                                                  |
  19. ;;;----------------------------------------------------------------------------------+
  20.  
  21. (in-package "CLIO-OPEN")
  22.  
  23. (export '(
  24.       buffer
  25.       buffer-insert
  26.       buffer-delete
  27.       buffer-subseq
  28.       buffer-length
  29.       buffer-number-lines
  30.  
  31.       mark
  32.       move-mark
  33.       )
  34.     'clio-open)
  35.  
  36.  
  37. ;;; Define base character type for either CLtL or ANSI Common Lisp variants.
  38. (deftype buffer-character ()
  39.   #+(or explorer ansi-common-lisp) 'base-character
  40.   #-(or explorer ansi-common-lisp) 'string-char)
  41.  
  42. ;;; PCL can't specialize methods on structure classes. Use defstruct*
  43. ;;; to define such structures.
  44. (defmacro defstruct* (name &rest slots)
  45.   #-pcl
  46.   `(defstruct ,name ,@slots)
  47.   
  48.   #+pcl
  49.   (flet ((translate-slot (slot &optional initform &key (type t))
  50.        `(,slot
  51.          :initform ,initform
  52.          :type ,type
  53.          :initarg ,(intern (string slot) (find-package :keyword))
  54.          :accessor ,(intern (format nil "~a-~a" name slot)))))
  55.     (let ((pred (intern (format nil "~a-P" name))))
  56.       `(progn
  57.     (defclass ,name ()
  58.       ,(mapcar #'(lambda (x) (apply #'translate-slot x)) slots)
  59.       (:metaclass structure-class))
  60.     (defmethod ,pred ((z t))  nil)
  61.     (defmethod ,pred ((z ,name)) t)
  62.     (defun ,(intern (format nil "MAKE-~a" name)) (&rest args)
  63.       (apply #'make-instance ',name args))))))
  64.  
  65.  
  66. ;;;----------------------------------------------------------------------------+
  67. ;;;                                                                            |
  68. ;;;                               Vector Functions                             |
  69. ;;;                                                                            |
  70. ;;;----------------------------------------------------------------------------+
  71.  
  72. (defconstant *vector-adjust-factor* 1.20
  73.   "Factor used to increase the size  of a vector when calling adjust-array.")
  74.  
  75. (defun vector-insert (vector start &optional from (from-start 0) (count 1))
  76.   "Insert COUNT new elements into VECTOR beginning at the START index.
  77. New elements, if any, are taken from FROM vector, beginning at FROM-START.
  78. The VECTOR index of the end of the inserted elements is returned. The
  79. second return value is the (possibly adjusted) vector."
  80.   (declare (type vector           vector)
  81.        (type (or null vector) from)
  82.        (type (integer 0 *)    start from-start count))
  83.   (declare (values end vector))
  84.   (let* ((start      (min (max start 0) (fill-pointer vector)))
  85.      (new-length (+ (fill-pointer vector) count))
  86.      (end        (+ start count)))
  87.     
  88.     ;; Extend vector, if necessary
  89.     (when (> new-length (array-dimension vector 0))
  90.       (setf vector (adjust-array vector (ceiling (* *vector-adjust-factor* new-length))
  91.                  :fill-pointer (fill-pointer vector))))    ; Keep fill-pointer.
  92.     (setf (fill-pointer vector) new-length)
  93.     
  94.     ;; Make room for new elements
  95.     (replace vector vector :start1 end :end1 new-length :start2 start)
  96.     
  97.     ;; Insert new elements
  98.     (when from
  99.       (replace vector from :start1 start :start2 from-start :end2 (+ from-start count)))
  100.  
  101.     (values end vector)))
  102.  
  103. (defun vector-delete (vector &optional (start 0) (end nil))
  104.   "Deletes the substring from START to END from the VECTOR."
  105.   (let ((start (min (max start 0) (fill-pointer vector))))
  106.     (if end
  107.     ;; Delete from middle
  108.     (let* ((end (min (max end start) (fill-pointer vector))))
  109.       (replace vector vector :start1 start :start2 end)
  110.       (decf (fill-pointer vector) (- end start)))
  111.  
  112.     ;; Delete from end
  113.     (setf (fill-pointer vector) start))))
  114.  
  115.  
  116. (defun vector-append (vector from &optional (from-start 0) from-end)
  117.   (let* ((old-length (fill-pointer vector))
  118.      (new-length (+ old-length (- (or from-end (length from)) from-start))))
  119.     (when (> new-length (array-dimension vector 0))
  120.       (adjust-array vector new-length :fill-pointer old-length))    ; Keep fill-pointer.
  121.     (setf (fill-pointer vector) new-length)
  122.     (replace vector from :start1 old-length :start2 from-start :end2 from-end)))
  123.  
  124.  
  125. ;;;----------------------------------------------------------------------------+
  126. ;;;                                                                            |
  127. ;;;                                 buffer-line                                |
  128. ;;;                                                                            |
  129. ;;;----------------------------------------------------------------------------+
  130.  
  131.  
  132. (defconstant *minimum-buffer-line-length* 10
  133.   "Initial string dimension for a new buffer-line.")
  134.  
  135. (defstruct* buffer-line
  136.   (chars (make-array *minimum-buffer-line-length*
  137.              :adjustable   t
  138.              :fill-pointer 0
  139.              :element-type 'buffer-character)
  140.      :type (array buffer-character)))
  141.  
  142. (defmethod print-object ((buffer-line buffer-line) stream)
  143.   (format stream "#<BUFFER-LINE ~a>" (substitute (code-char 0) #\newline (buffer-line-chars buffer-line))))
  144.  
  145. (defgeneric buffer-line-insert (buffer-line chars position &key start end)
  146.   (:documentation
  147.     "Inserts the substring of CHARS given by START/END into the BUFFER-LINE at the given POSITION.
  148. Returns the position at the end of the inserted CHARS."))
  149.  
  150. (defmethod buffer-line-insert ((buffer-line buffer-line) chars (position null) &key (start 0) end)
  151.   (buffer-line-insert
  152.     buffer-line chars (length (buffer-line-chars buffer-line))
  153.     :start start
  154.     :end end))
  155.  
  156. (defmethod buffer-line-insert ((buffer-line buffer-line) (chars string) (position integer) &key (start 0) end)
  157.   (multiple-value-bind (position chars)
  158.       (vector-insert
  159.     (buffer-line-chars buffer-line) position chars
  160.     start
  161.     (- (or end (length chars)) start))
  162.     (setf (buffer-line-chars buffer-line) chars)
  163.     position))
  164.  
  165. (defmethod buffer-line-insert ((buffer-line buffer-line) (char character) (position integer) &key (start 0) end)
  166.   (declare (ignore start end))
  167.   (let ((chars (buffer-line-chars buffer-line)))
  168.     (multiple-value-bind (new-position chars) (vector-insert chars position)
  169.       (setf (elt chars position) char)
  170.  
  171.       (setf (buffer-line-chars buffer-line) chars)
  172.       new-position)))
  173.  
  174. (defmethod buffer-line-insert ((buffer-line buffer-line) (chars buffer-line) position &key (start 0) end) 
  175.   (buffer-line-insert buffer-line (buffer-line-chars chars) position :start start :end end))
  176.  
  177. (defun buffer-line-delete (buffer-line &optional (start 0) (end nil))
  178.   "Deletes the substring from START to END from the BUFFER-LINE."
  179.   (vector-delete (buffer-line-chars buffer-line) start end))
  180.  
  181.  
  182. ;;;----------------------------------------------------------------------------+
  183. ;;;                                                                            |
  184. ;;;                                    buffer                                  |
  185. ;;;                                                                            |
  186. ;;;----------------------------------------------------------------------------+
  187.  
  188.  
  189. (defconstant *minimum-buffer-length* 2
  190.   "Initial dimension of lines array for a new buffer.")
  191.  
  192. (defstruct* buffer
  193.   (lines (make-array *minimum-buffer-length*
  194.              :adjustable   t
  195.              :fill-pointer 0
  196.              :element-type 'buffer-line)
  197.      :type array))
  198.  
  199. (defmethod print-object ((buffer buffer) stream)
  200.   (format stream "#<BUFFER :LENGTH ~d>" (fill-pointer (buffer-lines buffer))))
  201.  
  202.  
  203.  
  204. ;;;----------------------------------------------------------------------------+
  205. ;;;                                                                            |
  206. ;;;                                     mark                                   |
  207. ;;;                                                                            |
  208. ;;;----------------------------------------------------------------------------+
  209.  
  210.  
  211. (defstruct* mark
  212.   (buffer     nil)
  213.   (line-index  0   :type (integer 0 *))
  214.   (index       0   :type (integer 0 *)))
  215.  
  216. ;; A composite type including all forms of positioning within text
  217. (deftype text-mark () '(or null            ; end-of-buffer
  218.                integer            ; string array index
  219.                mark))        ; multiline-text mark
  220.  
  221. (defun mark-line (mark)
  222.   "Return the BUFFER-LINE indicated by the MARK."
  223.   (elt (buffer-lines (mark-buffer mark)) (mark-line-index mark)))
  224.  
  225. (defgeneric move-mark (mark line &optional index)
  226.   (:documentation "Updates MARK to point to the given LINE/INDEX.
  227. The new MARK is returned."))
  228.  
  229. (defmethod move-mark ((mark mark) (line mark) &optional index)  
  230.   (setf (mark-buffer mark)     (or (mark-buffer line) (mark-buffer mark)) 
  231.     (mark-line-index mark) (mark-line-index line)
  232.     (mark-index mark)      (or index (mark-index line)))
  233.   mark)
  234.  
  235. (defmethod move-mark ((mark mark) (position integer) &optional (index nil index-p))
  236.   (if index-p
  237.       ;; Then POSITION is a line index and INDEX is a char index. 
  238.       (setf (mark-line-index mark) position
  239.         (mark-index mark)      index)
  240.  
  241.       ;; Else POSITION is a buffer index.
  242.       (buffer-position-mark (mark-buffer mark) position mark))
  243.   mark)
  244.  
  245. (defmethod move-mark (mark new-mark &optional index)
  246.   ;; This method allows move-mark to be used generically for all text-mark's
  247.   (declare (ignore mark))
  248.   (assert (not index) nil
  249.       "Text mark is not a mark object; only a single new text-mark value may be specified.")
  250.   new-mark)
  251.  
  252. (defmethod print-object ((mark mark) stream)
  253.   (format stream "#<MARK :LINE ~A :INDEX ~D>"
  254.       (if (and (mark-buffer mark)
  255.            (< (mark-line-index mark) (buffer-number-lines (mark-buffer mark))))
  256.           (mark-line mark)
  257.           (mark-line-index mark))
  258.       (mark-index mark)))
  259.  
  260. (defgeneric mark-equal (mark1 mark2)
  261.   (:documentation "Returns true if the marks point to the same buffer position."))
  262.  
  263. (defmethod mark-equal ((mark1 mark) (mark2 mark))
  264.   (and (eq (mark-buffer mark1)     (mark-buffer mark2))
  265.        (=  (mark-line-index mark1) (mark-line-index mark2))
  266.        (=  (mark-index mark1)      (mark-index mark2))))
  267.  
  268. (defmethod mark-equal (mark1 mark2) 
  269.   (eql mark1 mark2))
  270.  
  271. (defvar .temp-mark. (make-mark))
  272.  
  273. (defmethod mark-equal ((mark1 mark) mark2) 
  274.   (mark-equal mark1 (buffer-position-mark (mark-buffer mark1) mark2 .temp-mark.)))
  275.   
  276. (defmethod mark-equal (mark1 (mark2 mark)) 
  277.   (mark-equal (buffer-position-mark (mark-buffer mark2) mark1 .temp-mark.) mark2))
  278.  
  279. (defgeneric mark-range (buffer mark1 mark2)
  280.   (:documentation "Compares the marks and returns three values: the smaller mark, the larger mark,
  281. and the result of MARK-EQUAL."))
  282.  
  283. (defmethod mark-range (buffer (mark1 mark) (mark2 mark))
  284.   (declare (ignore buffer))
  285.   (assert (eq (mark-buffer mark1)     (mark-buffer mark2)) nil
  286.       "~s and ~s point to different buffers.")
  287.   (cond
  288.     ((< (mark-line-index mark1) (mark-line-index mark2))
  289.      (values mark1 mark2 nil))
  290.  
  291.     ((< (mark-line-index mark2) (mark-line-index mark1))
  292.      (values mark2 mark1 nil))
  293.  
  294.     ((< (mark-index mark1) (mark-index mark2))
  295.      (values mark1 mark2 nil))
  296.  
  297.     ((< (mark-index mark2) (mark-index mark1))
  298.      (values mark2 mark1 nil))
  299.  
  300.     (t
  301.      (values mark1 mark2 t))))
  302.  
  303. (defmethod mark-range (buffer (mark1 mark) mark2)
  304.   (mark-range buffer mark1 (buffer-position-mark buffer mark2 .temp-mark.)))
  305.  
  306. (defmethod mark-range (buffer (mark1 integer) (mark2 integer))
  307.   (declare (ignore buffer))
  308.   (values (min mark1 mark2) (max mark1 mark2) (= mark1 mark2)))
  309.  
  310. (defmethod mark-range (buffer (mark1 integer) mark2)
  311.   (let ((mark2 (buffer-mark-position buffer mark2)))
  312.     (values (min mark1 mark2) (max mark1 mark2) (= mark1 mark2))))
  313.      
  314. (defmethod mark-range (buffer (mark1 null) mark2)
  315.     (mark-range buffer (buffer-length buffer) mark2))
  316.  
  317. (defgeneric buffer-mark-position (buffer mark)
  318.   (:documentation "Return a buffer index corresponding to the MARK."))
  319.  
  320. (defmethod buffer-mark-position (buffer (mark mark))
  321.   "Return a buffer index corresponding to the MARK."
  322.   (declare (ignore buffer))
  323.   (let ((index  0)
  324.     (buffer (mark-buffer mark))
  325.     (line   (mark-line-index mark)))
  326.     (assert buffer nil "Buffer not defined for ~a." mark)
  327.     (dotimes (i line) (incf index (buffer-length (buffer-line buffer i))))
  328.     (incf index (mark-index mark))))
  329.  
  330. (defmethod buffer-mark-position (buffer (mark integer))
  331.   (declare (ignore buffer))
  332.   mark)
  333.  
  334. (defmethod buffer-mark-position (buffer (mark null))
  335.   (buffer-length buffer))
  336.  
  337.  
  338.  
  339. ;;;----------------------------------------------------------------------------+
  340. ;;;                                                                            |
  341. ;;;                                  Utilities                                 |
  342. ;;;                                                                            |
  343. ;;;----------------------------------------------------------------------------+
  344.  
  345. (defun parse-source (source &key (start 0) end)
  346.   "Parse the substring of SOURCE given by START/END, returning:
  347.     1. The index behind the first #\newline, or END (whichever is smaller)
  348.     2. An array of buffer-line's containing all characters 
  349.        between the first and last #\newline's.
  350.     3. The index behind the last #\newline."
  351.   (declare (type string source))
  352.   (declare (values first-end buffer-lines tail-start))
  353.  
  354.   (let ((first-end (or (position #\newline source :start start :end end) end)))
  355.     (cond
  356.       ((eql first-end end) end)
  357.             
  358.       (:else
  359.        ;; Compute end of first line.
  360.        (incf first-end)
  361.        
  362.        ;; Build internal buffer-line's, if any.
  363.        (multiple-value-bind (buffer-lines tail-start)
  364.        (do ((next-end first-end) lines) (())
  365.          (setf start    next-end 
  366.            next-end (position #\newline source :start start :end end) 
  367.            next-end (when next-end (1+ next-end)))
  368.          
  369.          (unless next-end
  370.            (return (values lines start)))
  371.          
  372.          (unless lines
  373.            (setf lines (make-array *minimum-buffer-length*
  374.                        :adjustable   t
  375.                        :fill-pointer 0
  376.                        :element-type 'buffer-line)))
  377.          
  378.          (let ((buffer-line (make-buffer-line)))
  379.            (buffer-line-insert buffer-line source 0 :start start :end next-end) 
  380.            (vector-push-extend buffer-line lines)))
  381.      (values first-end buffer-lines tail-start))))))
  382.  
  383.  
  384.  
  385. ;;;----------------------------------------------------------------------------+
  386. ;;;                                                                            |
  387. ;;;                                buffer-insert                               |
  388. ;;;                                                                            |
  389. ;;;----------------------------------------------------------------------------+
  390.  
  391. (defgeneric buffer-insert (buffer chars position &key start end)
  392.   (:documentation
  393.     "Inserts the substring of CHARS given by START/END into the BUFFER 
  394. at the given POSITION and returns the updated POSITION."))
  395.  
  396.  
  397. ;; buffer-line methods ----------------------------------------
  398.  
  399. (defmethod buffer-insert ((buffer buffer-line) chars (position mark) &key (start 0) end)
  400.   (assert (eq buffer (mark-buffer position)) nil "~s is not a mark for ~s." position buffer)
  401.   (move-mark position 0 (buffer-insert buffer chars (mark-index position) :start start :end end)))
  402.  
  403. (defmethod buffer-insert ((buffer buffer-line) chars (position null) &key (start 0) end)
  404.   (buffer-insert buffer chars (length (buffer-line-chars buffer)) :start start :end end))
  405.  
  406. (defmethod buffer-insert ((buffer buffer-line) (chars string) (position integer) &key (start 0) end)
  407.   (assert (not (find #\newline chars :start start :end end)) ()
  408.       "Can't insert #\NEWLINE into a one-line buffer.")
  409.   (buffer-line-insert buffer chars position :start start :end end))
  410.  
  411. (defmethod buffer-insert ((buffer buffer-line) (char character) (position integer) &key (start 0) end)
  412.   (declare (ignore start end))
  413.   (assert (not (eql char #\newline)) ()
  414.       "Can't insert #\NEWLINE into a one-line buffer.")
  415.   (buffer-line-insert buffer char position))
  416.  
  417.  
  418. ;; buffer methods ----------------------------------------------
  419.  
  420. (defmethod buffer-insert ((buffer buffer) (chars string) (position mark) &key (start 0) end)
  421.   (assert (eq buffer (mark-buffer position)) nil "~s is not a mark for ~s." position buffer)
  422.   
  423.   (when (plusp (length chars))
  424.     (multiple-value-bind (head lines tail) (parse-source chars :start start :end end)
  425.       (let*
  426.     ((line        (mark-line-index position))
  427.      (insert-line (buffer-line buffer line))
  428.           
  429.      ;; Insert head chars at mark position     
  430.      (end-head    (buffer-line-insert
  431.             insert-line chars (mark-index position)
  432.             :start start :end head))
  433.      
  434.      ;; Initialize final line/index.
  435.      (newline-p   (not (eql head end)))
  436.      (index       (if
  437.             ;; Does insert end on another line?
  438.             (cond              
  439.               (lines
  440.                ;; Insert following lines into buffer line array.
  441.                (multiple-value-bind (position vector)
  442.                    (vector-insert (buffer-lines buffer) (1+ line) lines 0 (length lines))
  443.                  (setf (buffer-lines buffer) vector)
  444.                  (setf line position)))
  445.               
  446.               (newline-p
  447.                (incf line)))
  448.             
  449.             ;; Yes, restart index at beginning of line.
  450.             0
  451.             
  452.             ;; No, final index is end of head chars.
  453.             end-head)))
  454.  
  455.     ;; Handle source chars after inserted newline.
  456.     (when newline-p      
  457.       (let* ((buffer-lines       (buffer-lines buffer))
  458.          (insert-line-chars  (buffer-line-chars insert-line))
  459.          (insert-line-length (length insert-line-chars))
  460.          (prev-tail-p        (< end-head insert-line-length)))
  461.         
  462.         ;; Add a new line when...
  463.         (when
  464.           (or
  465.         ;; ... tail of insert line ends in #\newline, or...
  466.         (and prev-tail-p
  467.              (eql #\newline (elt insert-line-chars (1- insert-line-length))))  
  468.         
  469.         ;; ... there's something to add at the end of the buffer.
  470.         (and (>= line (length buffer-lines)) (or tail prev-tail-p)))
  471.           
  472.           (multiple-value-bind (position buffer-lines) (vector-insert buffer-lines line)
  473.         (declare (ignore position))
  474.         (setf (buffer-lines buffer) buffer-lines)
  475.         (setf (elt buffer-lines line) (make-buffer-line))))
  476.         
  477.         (let ((next-line (elt buffer-lines line)))        
  478.           ;; Insert source tail chars at beginning of next line.    
  479.           (when tail      
  480.         (setf index (buffer-line-insert next-line chars index :start tail :end end)))
  481.           
  482.           ;; Move previous tail of insert line, if necesssary.
  483.           (when prev-tail-p
  484.         (buffer-line-insert next-line insert-line index :start end-head)
  485.         (buffer-line-delete insert-line end-head)))))
  486.  
  487.     ;; Return position at end of inserted chars
  488.     (move-mark position line index))))
  489.       
  490.   position)
  491.  
  492. (defmethod buffer-insert ((buffer buffer) (char character) (position mark) &key (start 0) end)
  493.   (declare (ignore start end))
  494.   (assert (eq buffer (mark-buffer position)) nil "~s is not a mark for ~s." position buffer)
  495.   
  496.   (let*
  497.     ((line               (mark-line-index position))
  498.      (insert-line        (buffer-line buffer line))
  499.      (end                (buffer-line-insert insert-line char (mark-index position)))
  500.      (index              (if
  501.                ;; Does insert end on another line?
  502.                (when (eql char #\newline) (incf line))
  503.                
  504.                ;; Yes, restart index at beginning of line.
  505.                0
  506.                
  507.                ;; No 
  508.                end))
  509.      (insert-line-length (length (buffer-line-chars insert-line))))
  510.     
  511.     ;; ;; Is there something behind an inserted newline?
  512.     (when (and (eql char #\newline) (< end insert-line-length))
  513.       
  514.       (let ((buffer-lines (buffer-lines buffer)))    
  515.     ;; Add a new line when...
  516.     (when
  517.       (or
  518.         ;; ... tail of insert line ends in #\newline, or...
  519.         (eql #\newline (elt (buffer-line-chars insert-line) (1- insert-line-length)))
  520.         
  521.         ;; ... we're at the end of the buffer.
  522.         (>= line (length buffer-lines)))
  523.       
  524.       (multiple-value-bind (position buffer-lines) (vector-insert buffer-lines line)
  525.         (declare (ignore position))
  526.         (setf (buffer-lines buffer) buffer-lines) 
  527.         (setf (elt buffer-lines line) (make-buffer-line))))
  528.     
  529.     ;; Move previous tail of insert line
  530.     (buffer-line-insert (elt buffer-lines line) insert-line index :start end)
  531.     (buffer-line-delete insert-line end)))
  532.     
  533.     ;; Return position at end of inserted chars
  534.     (move-mark position line index)))
  535.  
  536. (defmethod buffer-insert ((buffer buffer) chars (position null) &key (start 0) end)
  537.   (buffer-insert buffer chars (buffer-position-mark buffer position .temp-mark.) :start start :end end)
  538.   nil)
  539.  
  540. (defmethod buffer-insert ((buffer buffer) chars (position integer) &key (start 0) end)
  541.   (buffer-insert buffer chars (buffer-position-mark buffer position .temp-mark.) :start start :end end)
  542.   (+ position (if (characterp chars) 1 (length chars))))
  543.  
  544.  
  545. (defun buffer-position-mark (buffer position &optional mark)
  546.   "Return a MARK pointing at the given POSITION in the BUFFER. If a MARK
  547.    is given, then it is updated and returned; otherwise a new mark is returned."
  548.   (declare (type buffer                  buffer)
  549.        (type (or null (integer 0 *)) position))
  550.   (declare (values mark))
  551.   (check-type position (or null (integer 0 *)))
  552.   (check-type buffer buffer)
  553.   
  554.   (let ((mark (or mark (make-mark))))
  555.     (setf (mark-buffer mark) buffer)            
  556.     
  557.     (multiple-value-bind (line-index index)
  558.     (when position
  559.       ;; Search for line/index corresponding to position.
  560.       (do* ((lines  (buffer-lines buffer))
  561.         (nlines (length lines))
  562.         (line   0 (1+ line)))
  563.           
  564.           ;; Return nil if position is past end of buffer.
  565.           ((>= line nlines))
  566.         
  567.         (let* ((chars  (buffer-line-chars (elt lines line)))
  568.            (nchars (length chars)))          
  569.           (when
  570.         (or
  571.           ;; Position within current line?
  572.           (< position nchars)
  573.           
  574.           ;; Position at end of line not ending in #\newline?
  575.           (and (= position nchars)
  576.                (or (zerop nchars)
  577.                (not (eql #\newline (elt chars (1- nchars)))))))
  578.         
  579.         ;; Return valid line/index.
  580.         (return (values line position)))
  581.           
  582.           (decf position nchars))))
  583.       
  584.       ;; Valid line/index found?
  585.       (unless line-index
  586.     (multiple-value-setq (line-index index)
  587.       
  588.       ;; No, return line/index for end of buffer.
  589.       (let* ((lines    (buffer-lines buffer))
  590.          (max-line (1- (length lines)))
  591.          (line     (unless (minusp max-line) (buffer-line-chars (elt lines max-line))))
  592.          (max-char (when line (length line))))
  593.         (cond
  594.           ((or
  595.          ;; No lines?
  596.          (minusp max-line)
  597.          
  598.          ;; Last line ends in #\newline?
  599.          (when (plusp max-char)
  600.            (eql #\newline (elt line (1- max-char)))))
  601.            
  602.            ;; Add empty line to empty buffer.
  603.            (vector-push-extend (make-buffer-line) lines)
  604.            
  605.            ;; End of buffer is begining of new line.
  606.            (values (1+ max-line) 0))
  607.           
  608.           (:else
  609.            (values max-line max-char))))))
  610.       (move-mark mark line-index index))))
  611.  
  612.  
  613.  
  614. ;;;----------------------------------------------------------------------------+
  615. ;;;                                                                            |
  616. ;;;                                buffer-delete                               |
  617. ;;;                                                                            |
  618. ;;;----------------------------------------------------------------------------+
  619.  
  620. (defgeneric buffer-delete (buffer start end)
  621.   (:documentation
  622.     "Deletes the chars from START to END from the BUFFER."))
  623.  
  624.  
  625. ;; buffer-line methods ----------------------------------------
  626.  
  627. (defmethod buffer-delete ((buffer buffer-line) (start mark) end)
  628.   (buffer-delete buffer (mark-index start) end))
  629.  
  630. (defmethod buffer-delete ((buffer buffer-line) start (end mark))
  631.   (buffer-delete buffer start (mark-index end)))
  632.  
  633. (defmethod buffer-delete ((buffer buffer-line) (start mark) (end mark))
  634.   (buffer-delete buffer (mark-index start) (mark-index end)))
  635.  
  636. (defmethod buffer-delete ((buffer buffer-line) (start integer) end)
  637.   (vector-delete (buffer-line-chars buffer) start end))
  638.  
  639.  
  640.  
  641. ;; buffer methods ----------------------------------------------
  642.  
  643. (defmethod buffer-delete ((buffer buffer) (start integer) end)
  644.   (buffer-delete buffer (buffer-position-mark buffer start .temp-mark.) end))
  645.  
  646. (defmethod buffer-delete ((buffer buffer) start (end integer))
  647.   (buffer-delete buffer start (buffer-position-mark buffer end .temp-mark.)))
  648.  
  649. (defmethod buffer-delete ((buffer buffer) (start mark) (end null))
  650.   (let ((line-index (mark-line-index start))
  651.     (lines      (buffer-lines buffer)))
  652.     (buffer-line-delete (elt lines line-index) (mark-index start))
  653.     (vector-delete lines (1+ line-index))))
  654.  
  655. (defmethod buffer-delete ((buffer buffer) (start mark) (end mark))
  656.   (assert (eq buffer (mark-buffer start)) nil
  657.       "Start mark does not point to ~s." buffer)
  658.   (assert (eq buffer (mark-buffer end)) nil
  659.       "End mark does not point to ~s." buffer)
  660.   (let*
  661.     ((lines       (buffer-lines buffer))
  662.      
  663.      (sli         (mark-line-index start))
  664.      (start-line  (elt lines sli))
  665.      (eli         (mark-line-index end)) 
  666.      
  667.      (start-start (mark-index start))
  668.      (start-end   (when (= sli eli) (mark-index end))))
  669.  
  670.     ;;
  671.     ;; Assert: start-end is non-nil iff start/end are on same line.
  672.     ;;
  673.     (assert (or (> eli sli) (and start-end (>= start-end start-start)))
  674.         nil "Start mark is past end mark.")
  675.  
  676.     ;; Delete chars from start line.
  677.     (buffer-line-delete start-line start-start start-end)
  678.  
  679.     (unless start-end
  680.       ;; Move chars up from end line and discard end line.
  681.       (buffer-line-insert
  682.     start-line (buffer-line-chars (elt lines eli)) nil
  683.     :start (mark-index end))
  684.       (vector-delete lines eli (1+ eli))
  685.       
  686.       ;; Delete any lines between start and end marks.
  687.       (vector-delete lines (1+ sli) eli))))
  688.  
  689.  
  690. ;;;----------------------------------------------------------------------------+
  691. ;;;                                                                            |
  692. ;;;                                buffer-length                               |
  693. ;;;                                                                            |
  694. ;;;----------------------------------------------------------------------------+
  695.  
  696. (defgeneric buffer-length (buffer)
  697.   (:documentation
  698.     "Returns the number of characters in the BUFFER."))
  699.  
  700. (defmethod buffer-length ((buffer buffer-line))
  701.   (length (buffer-line-chars buffer)))
  702.  
  703. (defmethod buffer-length ((buffer buffer))
  704.   (let ((length 0)
  705.     (lines  (buffer-lines buffer)))
  706.     (dotimes (i (length lines) length)
  707.       (incf length (length (buffer-line-chars (elt lines i))))))) 
  708.     
  709.  
  710.  
  711. ;;;----------------------------------------------------------------------------+
  712. ;;;                                                                            |
  713. ;;;                                buffer-subseq                               |
  714. ;;;                                                                            |
  715. ;;;----------------------------------------------------------------------------+
  716.  
  717. (defgeneric buffer-subseq (buffer start end)
  718.   (:documentation
  719.     "Returns the BUFFER substring given by START and END."))
  720.  
  721.  
  722. ;; buffer-line methods ----------------------------------------
  723.  
  724. (defmethod buffer-subseq ((buffer buffer-line) (start mark) end)
  725.   (buffer-subseq buffer (mark-index start) end))
  726.  
  727. (defmethod buffer-subseq ((buffer buffer-line) start (end mark))
  728.   (buffer-subseq buffer start (mark-index end)))
  729.  
  730. (defmethod buffer-subseq ((buffer buffer-line) (start integer) (end integer))
  731.   (subseq (buffer-line-chars buffer) start end))
  732.  
  733. (defmethod buffer-subseq ((buffer buffer-line) (start integer) (end null))
  734.   (subseq (buffer-line-chars buffer) start end))
  735.  
  736.  
  737. ;; buffer methods ----------------------------------------------
  738.  
  739. (defmethod buffer-subseq ((buffer buffer) (start integer) end)
  740.   (buffer-subseq buffer (buffer-position-mark buffer start .temp-mark.) end))
  741.  
  742. (defmethod buffer-subseq ((buffer buffer) start (end integer))
  743.   (buffer-subseq buffer start (buffer-position-mark buffer end .temp-mark.)))
  744.  
  745. (defmethod buffer-subseq ((buffer buffer) start (end null))
  746.   (buffer-subseq buffer start (buffer-length buffer)))
  747.  
  748. (defmethod buffer-subseq ((buffer buffer) (start mark) (end mark))
  749.   (assert (eq buffer (mark-buffer start)) nil
  750.       "Start mark does not point to ~s." buffer)
  751.   (assert (eq buffer (mark-buffer end)) nil
  752.       "End mark does not point to ~s." buffer)
  753.  
  754.   (let ((start-line  (mark-line-index start))
  755.     (start-index (mark-index start))
  756.     (end-line    (mark-line-index end))
  757.     (end-index   (mark-index end)))
  758.     
  759.     (assert (or (> end-line start-line)
  760.         (and (= end-line start-line) (>= end-index start-index)))
  761.         nil "Start mark is past end mark.")
  762.     
  763.     (let ((subseq (make-array *minimum-buffer-line-length*
  764.                   :adjustable   t
  765.                   :fill-pointer 0
  766.                   :element-type 'buffer-character)))
  767.       (do
  768.     ((lines (buffer-lines buffer))
  769.      (max   (length (buffer-lines buffer)))
  770.      (line  start-line  (1+ line))
  771.      (start start-index 0))
  772.     
  773.     ((or (> line end-line) (>= line max)))
  774.     
  775.     (vector-append subseq (buffer-line-chars (elt lines line))
  776.                start
  777.                (when (= line end-line) end-index)))
  778.       
  779.       subseq)))
  780.  
  781.  
  782. ;;;----------------------------------------------------------------------------+
  783. ;;;                                                                            |
  784. ;;;                            buffer-number-lines                             |
  785. ;;;                                                                            |
  786. ;;;----------------------------------------------------------------------------+
  787.  
  788. (defgeneric buffer-number-lines (buffer)
  789.   (:documentation
  790.     "Returns the number of lines in the BUFFER."))
  791.  
  792.  
  793. (defmethod buffer-number-lines ((buffer buffer-line))
  794.   (if (zerop (length (buffer-line-chars buffer))) 0 1))
  795.  
  796. (defmethod buffer-number-lines ((buffer buffer))
  797.   (length (buffer-lines buffer)))
  798.  
  799.  
  800. ;;;----------------------------------------------------------------------------+
  801. ;;;                                                                            |
  802. ;;;                                buffer-line                                 |
  803. ;;;                                                                            |
  804. ;;;----------------------------------------------------------------------------+
  805.  
  806. (defgeneric buffer-line (buffer i)
  807.   (:documentation
  808.     "Returns the i'th buffer-line in the BUFFER."))
  809.  
  810.  
  811. (defmethod buffer-line ((buffer buffer-line) i)
  812.   (when (and (plusp (length (buffer-line-chars buffer))) (zerop i))
  813.     buffer))
  814.  
  815. (defmethod buffer-line ((buffer buffer) i)
  816.   (elt (buffer-lines buffer) i))
  817.  
  818.  
  819.  
  820. ;;;----------------------------------------------------------------------------+
  821. ;;;                                                                            |
  822. ;;;                             buffer-move-mark                               |
  823. ;;;                                                                            |
  824. ;;;----------------------------------------------------------------------------+
  825.  
  826. (defgeneric buffer-move-mark (buffer mark &key lines chars)
  827.   (:documentation "Move the MARK by the given number of LINES and CHARS and return the updated MARK."))
  828.  
  829. (defmethod buffer-move-mark ((buffer buffer-line) (mark integer) &key (lines 0) (chars 0))
  830.   (assert (zerop lines) nil "Cannot change line within ~a." buffer)
  831.   (max 0 (min (+ mark chars) (length (buffer-line-chars buffer)))))
  832.  
  833. (defmethod buffer-move-mark ((buffer buffer-line) (mark null) &key (lines 0) (chars 0))
  834.   (buffer-move-mark buffer (length (buffer-line-chars buffer)) :lines lines :chars chars))
  835.  
  836. (defvar .new-mark. (make-mark))
  837.  
  838. (defmethod buffer-move-mark ((buffer buffer) (mark mark) &key (lines 0) (chars 0))
  839.   (let* ((blines   (buffer-lines buffer))
  840.      (max-line (1- (length blines))))
  841.  
  842.     (move-mark .new-mark. mark)
  843.       
  844.     (unless (zerop lines)
  845.       (setf (mark-line-index .new-mark.)
  846.         (max 0 (min max-line (+ (mark-line-index .new-mark.) lines))))
  847.  
  848.       ;; Trying to move past end of line?
  849.       (let* ((line (elt blines (mark-line-index .new-mark.)))
  850.          (max  (1- (buffer-length line))))
  851.     (unless (or (minusp max) (eql #\newline (elt (buffer-line-chars line) max)))
  852.       (incf max))
  853.     (when (> (mark-index .new-mark.) max)
  854.       (setf (mark-index .new-mark.) (max max 0)))))
  855.       
  856.     (unless (zerop chars)
  857.       (setf (mark-index .new-mark.)
  858.         (do ((position (+ (mark-index .new-mark.) chars))
  859.          max)
  860.         (())
  861.           (cond
  862.         ;; Trying to move before start of line?
  863.         ((< position 0)
  864.          (cond
  865.            ;; Trying to move before first character in buffer?
  866.            ((zerop (mark-line-index .new-mark.))
  867.             ;; Yes, stop at first character.
  868.             (setf position 0))
  869.              
  870.            ;; No, move to previous line.
  871.            (t
  872.             (decf (mark-line-index .new-mark.))
  873.             (incf position (buffer-length (elt blines (mark-line-index .new-mark.)))))))
  874.  
  875.         ;; Trying to move past end of (not the last) line?
  876.         ((and
  877.           (>= position (setf max (buffer-length (elt blines (mark-line-index .new-mark.)))))
  878.           (< (mark-line-index .new-mark.) max-line))           
  879.          ;; Yes, move to next line.
  880.          (decf position max)
  881.          (incf (mark-line-index .new-mark.)))
  882.  
  883.         ;; Trying to move past end of buffer?
  884.         ((> position max)
  885.          ;; Yes, stop at end of buffer.
  886.          (setf position max))
  887.           
  888.         (t
  889.          (return position))))))
  890.     .new-mark.))
  891.  
  892.  
  893. ;;;----------------------------------------------------------------------------+
  894. ;;;                                                                            |
  895. ;;;                            buffer-text-extents                             |
  896. ;;;                                                                            |
  897. ;;;----------------------------------------------------------------------------+
  898.  
  899. (defgeneric buffer-text-extents (buffer font start end &key translate)
  900.   (:documentation "Return the width, height, ascent, and descent of the given substring of the BUFFER."))
  901.  
  902. (defmethod buffer-text-extents ((buffer buffer-line) font start end &key translate)
  903.   (multiple-value-bind (width a d l r ascent descent)
  904.       (text-extents font (buffer-line-chars buffer)
  905.             :start start :end end :translate translate)
  906.     (declare (ignore a d l r))
  907.     (values width (+ ascent descent) ascent descent)))
  908.  
  909.  
  910. ;;;----------------------------------------------------------------------------+
  911. ;;;                                                                            |
  912. ;;;                          buffer-sol/eol                                    |
  913. ;;;                                                                            |
  914. ;;;----------------------------------------------------------------------------+
  915.  
  916. (defgeneric buffer-sol (buffer position)
  917.   (:documentation "Return the position in BUFFER at the start of the line containing POSITION."))
  918.  
  919. (defmethod buffer-sol ((buffer buffer-line) position)
  920.   (declare (ignore position))
  921.   0)
  922.  
  923. (let ((mark (make-mark)))
  924.   (defmethod buffer-sol ((buffer buffer) (position mark))
  925.     (setf (mark-buffer mark) buffer)
  926.     (move-mark mark (mark-line-index position) 0))
  927.  
  928.   (defmethod buffer-sol ((buffer buffer) position)
  929.     (buffer-position-mark buffer position mark)
  930.     (setf (mark-index mark) 0)
  931.     mark))
  932.  
  933.  
  934. (defgeneric buffer-eol (buffer position)
  935.   (:documentation "Return the position in BUFFER at the end of the line containing POSITION."))
  936.  
  937. (defmethod buffer-eol ((buffer buffer-line) position)
  938.   (declare (ignore position))
  939.   (buffer-length buffer))
  940.  
  941. (let ((mark (make-mark)))
  942.   (defmethod buffer-eol ((buffer buffer) (position mark))
  943.     (let*
  944.       ((line  (mark-line-index position))
  945.        (bline (elt (buffer-lines buffer) line))
  946.        (max   (buffer-length bline))
  947.        (end   (if (and (plusp max) (eql #\newline (elt (buffer-line-chars bline) (1- max)))) (1- max) max)))
  948.  
  949.       (setf (mark-buffer mark) buffer)
  950.       (move-mark mark line end)))
  951.  
  952.   (defmethod buffer-eol ((buffer buffer) position)
  953.     (buffer-eol buffer (buffer-position-mark buffer position mark))))
  954.  
  955.